package io.wongxd.demo.mvi.core

import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

/**
 *
 * 基于BaseViewModel实现，用于实现 MVI 模式的使用
 * <p>
 * 需要指定 Intent（事件） 与 State（状态） 的泛型类型
 */
abstract class BaseISViewModel<I : IUiIntent, S : IUiState> : BaseVM() {

    // Channel 类似于阻塞队列，但它是挂起的，适用于协程之间的通信。它可以配置为不同的模式，如缓存大小和发送行为。适用于事件的生产者-消费者模型，如任务执行、消息传递等
    // 创建意图管道，容量无限大 （可以用Flow的监听效果，观察者模式改变之后就自动更新）
    // 为什么用Channel不用Flow，是因为只需要单向流动就行了，Channel是单对单，Flow的单对多
    // Channel的发送send和接收receive,发出的事件只能被接收一次，接收之后就不能再次接收了，很适合这个场景。 万一屏幕旋转重建了也不会再度触发事件。
    // 页面事件的 Channel 分发
    private val _uiIntentFlow = Channel<I>(Channel.UNLIMITED)

    init {
        // 这里是通过Channel的方式自动分发的。
        viewModelScope.launch {
            //收集意图 （观察者模式改变之后就自动更新）用于协程通信的，所以需要在协程中调用
            _uiIntentFlow.consumeAsFlow().collect { intent ->
                handleIntent(intent)
            }
        }

    }

    private val _uiStateFlow by lazy { MutableStateFlow(initUiState()) }
    val uiStateFlow: StateFlow<S> by lazy { _uiStateFlow }

    // 更新State页面状态 (data class 类型)
    fun updateUiState(reducer: S.() -> S) {
        _uiStateFlow.update { reducer(_uiStateFlow.value) }
    }

    // 更新State页面状态 (sealed class 类型)
    fun sendUiState(s: S) {
        _uiStateFlow.value = s
    }

    // 发送页面事件
    fun sendUiIntent(uiIntent: I) {
        viewModelScope.launch {
            _uiIntentFlow.send(uiIntent)
        }
    }

    // 每个页面的 UiState 都不相同，必须实自己去创建
    protected abstract fun initUiState(): S

    // 每个页面处理的 UiIntent 都不同，必须实现自己页面对应的状态处理
    protected abstract fun handleIntent(intent: I)

}